package com.txc.common.config.mybatisplus.handler;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.extension.plugins.handler.MultiDataPermissionHandler;
import com.txc.common.config.mybatisplus.annotation.DataScope;
import com.txc.common.config.mybatisplus.constant.DataScopeConstants;
import com.txc.common.global.model.SysRole;
import com.txc.common.global.model.LoginUser;
import com.txc.common.utils.HttpUtils.HttpUtils;
import com.txc.common.utils.supply.UserContext;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;

import java.lang.reflect.Method;
import java.util.List;
import java.util.stream.Collectors;

import static com.txc.common.global.constant.NumConstant.ONE;
import static com.txc.common.global.constant.StrConstant.DOT;

/**
 * mybatis-plus数据权限处理器
 *
 * @ClassName: DataPermissionHandler
 * @Author: TXC
 * @Date: 2022-08-05 22:23
 **/
@Slf4j
public class MpDataPermissionHandler implements MultiDataPermissionHandler {
    private static final String _COUNT = "_COUNT";

    /**
     * 获取数据权限 SQL 片段。
     *
     * @param table             所执行的数据库表信息，可以通过此参数获取表名和表别名
     * @param where             原有的 where 条件信息
     * @param mappedStatementId Mybatis MappedStatement id 根据该参数可以判断具体执行方法:方法全路径
     * @return JSqlParser 条件表达式，返回的条件表达式会拼接在原有的表达式后面（不会覆盖原有的表达式）
     */
    @Override
    public Expression getSqlSegment(Table table, Expression where, String mappedStatementId) {
        try {
            Class<?> mapperClazz = Class.forName(mappedStatementId.substring(0, mappedStatementId.lastIndexOf(DOT)));
            String methodName = mappedStatementId.substring(mappedStatementId.lastIndexOf(DOT) + ONE);
            // 获取 DataScope注解 DataScope注解优先级：【类上 > 方法上】
            DataScope dataScopeAnnotationClazz = mapperClazz.getAnnotation(DataScope.class);
            if (ObjectUtils.isNotEmpty(dataScopeAnnotationClazz) && dataScopeAnnotationClazz.enabled()) {
                return buildDataScopeByAnnotation(dataScopeAnnotationClazz);
            }
            // 获取自身类中的所有方法，不包括继承。与访问权限无关
            Method[] methods = mapperClazz.getDeclaredMethods();
            for (Method method : methods) {
                DataScope dataScopeAnnotationMethod = method.getAnnotation(DataScope.class);
                if (ObjectUtils.isEmpty(dataScopeAnnotationMethod) || !dataScopeAnnotationMethod.enabled()) {
                    continue;
                }
                if (method.getName().equals(methodName) || (method.getName() + _COUNT).equalsIgnoreCase(methodName)) {
                    return buildDataScopeByAnnotation(dataScopeAnnotationMethod);
                }
            }
        } catch (ClassNotFoundException e) {
            log.error("【DataScopeHandlerPlus】获取数据权限失败，请检查是否配置了DataScope注解！", e);
        }
        return null;
    }


    /**
     * DataScope注解方式，拼装数据权限
     *
     * @param controllerDataScope 数据权限注解
     * @return 表达式
     */
    private Expression buildDataScopeByAnnotation(DataScope controllerDataScope) {
        // 获取当前的用户
        LoginUser currentUser = UserContext.getUser();
        if (ObjectUtil.isNotNull(currentUser)) {
            SysRole role = getUserDataScope(currentUser);
            List<Long> deptIdList = null;
            Long dataScopeCreateId = currentUser.getId();
            // 如果是超级管理员，则不过滤数据
            if (ObjectUtil.isNotNull(role) && !DataScopeConstants.DATA_SCOPE_ALL.equals(role.getDataScope())) {
                // 判断是否指定数据权限
                if (!controllerDataScope.defaultDataScope().equals(DataScopeConstants.DATA_SCOPE_SELF)) {
                    if (!DataScopeConstants.DATA_SCOPE_SELF.equals(role.getDataScope())) {
                        deptIdList = role.getDeptIds();
                    }
                }
                Expression expression = dataScopeFilter(controllerDataScope.tableAlias(),
                        controllerDataScope.deptScopeName(),
                        controllerDataScope.oneselfScopeName(), deptIdList, dataScopeCreateId);
                log.info("【DataScopeHandlerPlus】数据权限处理的sql语句:{}", expression);
                return expression;
            }
        }
        return null;
    }

    /**
     * 获取用户数据权限列表
     *
     * @param currentUser 用户信息
     * @return 数据权限列表
     */
    private SysRole getUserDataScope(LoginUser currentUser) {
        String permissionsCode = HttpUtils.getPermissionsCode();
        if (ObjectUtil.isNotNull(currentUser)) {
            List<SysRole> roles = currentUser.obtainRolesByPermissionsCode(permissionsCode);
            return currentUser.obtainRoleDataScope(roles);
        }
        return null;
    }


    /**
     * 拼装数据权限
     *
     * @param tableAlias        表别名
     * @param deptScopeName     部门限制范围的字段名称
     * @param oneselfScopeName  本人限制范围的字段名称
     * @param deptIdList        该用户部门id集合
     * @param dataScopeCreateId 数据权限本人ID
     * @return 表达式
     */
    private Expression dataScopeFilter(String tableAlias, String deptScopeName, String oneselfScopeName,
                                       List<Long> deptIdList, Long dataScopeCreateId) {
        // 构造部门in表达式
        InExpression deptIdInExpression = null;
        if (CollectionUtils.isNotEmpty(deptIdList)) {
            deptIdInExpression = new InExpression();
            ExpressionList<LongValue> deptIds = new ParenthesedExpressionList<>(deptIdList.stream().map(LongValue::new).collect(Collectors.toList()));
            // 设置左边的字段表达式，右边设置值。
            deptIdInExpression.setLeftExpression(buildColumn(tableAlias, deptScopeName));
            deptIdInExpression.setRightExpression(deptIds);
        }
        // 构造本人eq表达式
        EqualsTo oneselfEqualsTo = null;
        if (dataScopeCreateId != null) {
            oneselfEqualsTo = new EqualsTo();
            oneselfEqualsTo.withLeftExpression(buildColumn(tableAlias, oneselfScopeName));
            oneselfEqualsTo.setRightExpression(new LongValue(dataScopeCreateId));
        }
        if (deptIdInExpression != null && oneselfEqualsTo != null) {
            return new OrExpression(deptIdInExpression, oneselfEqualsTo);
        } else if (deptIdInExpression != null) {
            return deptIdInExpression;
        } else {
            return oneselfEqualsTo;
        }
    }

    /**
     * 构建Column
     *
     * @param tableAlias 表别名
     * @param columnName 字段名称
     * @return 带表别名字段
     */
    public static Column buildColumn(String tableAlias, String columnName) {
        if (StrUtil.isNotBlank(tableAlias)) {
            columnName = tableAlias + DOT + columnName;
        }
        return new Column(columnName);
    }

}
